#ifndef BIDK_CRASH_REPORT_H
#define BIDK_CRASH_REPORT_H

#include <cstdint>
#include <iostream>
#include <type_traits>
#include <signal.h>
#include "api/bidk_plugin_support.h"

class crash_report_t {
public:
    crash_report_t(int signum, const siginfo_t* info, const ucontext_t* context, const bidk_thread* thread_data);
    ~crash_report_t();

    void flush() noexcept;
    void write_header() noexcept;
    void write_siginfo() noexcept;
    void write_registers() noexcept;
    void write_cc_statistic() noexcept;
    void write_context_specific_info();
    void write_app_backtrace();

private:
    static const char* signal_name(int signum) noexcept;
    static const char* signal_code_name(int signum, int code) noexcept;

    bool from_code_cache() const noexcept;
    void write_bidk_backtrace();
    void write_cc_dump() noexcept;

    void create_crash_report_file() noexcept;
    void print_file_info() noexcept;
    void write_to_output() noexcept;

    struct hex
    {
        uint64_t value;
        int width;

        template <typename T>
        constexpr explicit hex(T v) noexcept
            : value(static_cast<uint64_t>(v)),
              width(sizeof(T) * 2)
        {}      
    };

    template <typename... types>
    void put(types&&... args) noexcept
    {
        (put_impl(args), ...);
    }

    template <typename T>
    std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, uint64_t> && !std::is_same_v<T, int64_t>>
    put_impl(T v) noexcept
    {
        if constexpr (std::is_signed_v<T>) {
            put_impl(static_cast<int64_t>(v));
        } else {
            put_impl(static_cast<uint64_t>(v));
        }
    }
    
    void put_impl(char c) noexcept;
    void put_impl(const char* s) noexcept;
    void put_impl(uint64_t v) noexcept;
    void put_impl(int64_t v) noexcept;
    void put_impl(const hex& h) noexcept;

    static constexpr size_t capasity_ = 8192;
    char buffer_[capasity_]{};
    char* p_{buffer_};
    int output_fd_{STDERR_FILENO};

    int signum_;
    const siginfo_t* siginfo_;
    const ucontext_t* context_;
    const bidk_thread* thread_data_;
};

#endif // BIDK_CRASH_REPORT_H